//
// Created by youle on 19-11-18.
//

#ifndef _STD_WAIT_QUEUE_H
#define _STD_WAIT_QUEUE_H

#include <list>
#include <queue>
#include <mutex>
#include <chrono>
#include <condition_variable>

template<class T>
class WaitStack
{
    const int MIN_WAIT_MS = 10;//min: 100ms
public:
    void Push(T & e)
    {
        std::unique_lock<std::mutex> lock(m_);
        q_.push(e);
        lock.unlock();
        c_.notify_one();
    }

    T & Front()
    {
        std::unique_lock<std::mutex> lock(m_);
        return q_.front();
    }

    T & PopWait(int tx = 200)
    {
        T t = make_shared<T>();
        std::unique_lock<std::mutex> lock(m_);
        if (q_.empty())
        {
            tx = tx > MIN_WAIT_MS ? tx : MIN_WAIT_MS;
            if (!c_.wait_for(lock, std::chrono::milliseconds(tx), [this]() {return !this->q_.empty();}))
            {
                return t; // timeout
            }
        }

        t = q_.front();
        q_.pop();
        return t;
    }

    T & PopClean(int tx = 200)
    {
        T t;
        std::unique_lock<std::mutex> lock(m_);
        if(q_.empty())
        {
            tx = tx > MIN_WAIT_MS ? tx : MIN_WAIT_MS;
            if (!c_.wait_for(lock, std::chrono::milliseconds(tx), [this]() {return !this->q_.empty();}))
            {
                return t; // timeout
            }
        }
        t = q_.front();
        q_.clear();
        return t;
    }

    int Len()
    {
        std::unique_lock<std::mutex> lock(m_);
        return q_.size();
    }

private:
    std::queue<T> q_;
    std::mutex m_;
    std::condition_variable c_;
};

#endif //VSE3_STD_WAIT_QUEUE_H
